package com.intretech.umsin.datasource.dao.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.intretech.umsin.common.utils.ConvertUtils;
import com.intretech.umsin.datasource.dao.KernDAO;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.OrderByElement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectVisitorAdapter;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.sql.*;
import java.util.*;

/**
 * @version V1.0
 * @ClassName: KernMyBatisDAO
 * @Description: 功能描述
 * @author: 李启联
 * @date: 2022-6-22 14:59
 * @Copyright: Copyright (c) 2022
 */
@Slf4j
public class KernMyBatisDAO implements KernDAO {


    private SqlSessionTemplate sqlSessionTemplate;

    public SqlSessionTemplate getSqlSessionTemplate() {
        return sqlSessionTemplate;
    }

    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

    public KernMyBatisDAO(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

    /**
     * 获取sqlSession
     * @return
     */
    public SqlSession getSqlSession(){
        return SqlSessionUtils.getSqlSession(sqlSessionTemplate.getSqlSessionFactory(),
                sqlSessionTemplate.getExecutorType(), sqlSessionTemplate.getPersistenceExceptionTranslator());
    }

    /**
     * 关闭sqlSession
     * @param session
     */
    public void closeSqlSession(SqlSession session){
        SqlSessionUtils.closeSqlSession(session, sqlSessionTemplate.getSqlSessionFactory());
    }

    /**
     * 解析sql到待执行的PreparedStatement中
     * @param conn 数据库连接对象
     * @param cmdText 待执行的sql语句
     * @param obj 执行参数列表
     * @return 待执行的PreparedStatement
     * @throws Exception
     */
    public  PreparedStatement prepareStatement(Connection conn, String cmdText, Object... obj){
        PreparedStatement preSm = null;
        Object[] o = obj;
        List params = new LinkedList();
        StringBuilder sb = new StringBuilder();
        String[] strings = (cmdText+" ").split("\\?");
        if (strings.length > 0) {
            for (int i = 0; i < strings.length -1; i++){
                if(obj[i] instanceof Collection){
                    List<String> ls = new ArrayList<String>();
                    Iterator iterator = ((Collection<?>) o[i]).iterator();
                    while(iterator.hasNext()){
                        ls.add("?");
                        params.add(iterator.next());
                    }
                    sb.append(strings[i] + StringUtils.join(ls,","));
                }else {
                    sb.append(strings[i] + "?");
                    params.add(o[i]);
                }
            }
            sb.append(strings[strings.length -1]);
        }else {
          sb.append(cmdText);
          params.add(o);
        }

        try {
            preSm = conn.prepareStatement(sb.toString());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        if (!CollectionUtils.isEmpty(params)) {
            for (int i = 0; i < params.size(); i++) {
                try {
                    preSm.setObject(i + 1, params.get(i));
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return preSm;
    }

    /**
     * 把ResultSet转化为List数组
     * @param rs ResultSet 数据集
     * @return List数组
     * @throws SQLException
     */
    public  List resultSetToList(ResultSet rs) throws SQLException {
        if (rs == null) {
            return Collections.EMPTY_LIST;
        }
        ResultSetMetaData md = rs.getMetaData(); // 得到结果集(rs)的结构信息，比如字段数、字段名等
        int columnCount = md.getColumnCount(); // 返回此 ResultSet 对象中的列数
        List list = new ArrayList();
        Map rowData = null;
        while (rs.next()) {
            rowData = new LinkedHashMap();//顺序输出
            for (int i = 1; i <= columnCount; i++) {
                rowData.put(md.getColumnName(i).toLowerCase(), rs.getObject(i));
            }
            list.add(rowData);
            //System.out.println("list:" + list.toString());
        }
        return list;
    }

    /**
     * 分页的形式返回SQL查询结果
     *
     * @param sql  查询的SQL语句 ;SQL语句形式：(select * from T where Id=? and Name=? 或者 select * from T where Id='1' and Name='A') <br/>
     *             示例：select CONCAT(T.col_1) as c1,T.col_2 from T
     * @param args 查询可变参数
     * @return 得到数据集合列表
     * @throws Exception
     */
    @Override
    public List<Map<String, Object>> queryForListMap(String sql, Object... args){
        List<Map<String, Object>> list = new ArrayList<>();
        PreparedStatement preSm = null;
        ResultSet resultSet = null;
        SqlSession session = getSqlSession();
        try {
            preSm = prepareStatement(session.getConnection(),sql,args);
            resultSet = preSm.executeQuery();
            list = resultSetToList(resultSet);
            log.info("执行数据库操作  执行语句:{} 参数列表:{}",sql,args);
        } catch (SQLException e) {
            log.info("执行数据库操作异常 {}  执行语句:{} 参数列表:{}",e,sql,args);
            e.printStackTrace();
        }finally {
            if(preSm!=null){
                try {
                    preSm.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            closeSqlSession(session);
        }
        return list;
    }

    /**
     * 通过sql查询，并把结果集填充到对应类型的实体对象集合中。<BR/>
     * 填充规则：[实体属性名称与结果集字段 	  1.不分大小写  2.驼峰格式相互可以转成 相对应],找到便填充。
     *
     * @param sql  查询的SQL语句 ;SQL语句形式：(select * from T where Id=? and Name=? 或者 select * from T where Id='1' and Name='A') <br/>
     * @param type 实体对象的类型（如：T.class）
     * @param args 查询可变参数
     * @return 实体对象集合
     * @throws Exception
     */
    @Override
    public <T> List<T> queryForList(String sql, Class<T> type, Object... args){
        List<Map<String,Object>> list = this.queryForListMap(sql, args);
        return ConvertUtils.dataMapListToObjects(list, type);
    }

    /**
     * 通过SQL查询语句、存储过程、本地sql模板文件得到第一条查询结果数据,数据以Map形式返回
     *
     * @param sql  SQL语句,SQL语句形式：(select * from T where Id=? and Name=?)
     * @param args 执行sql命令或存储过程时传入的可变参数
     * @return 第一条查询结果数据, 数据以Map形式返回
     * @throws Exception
     */
    @Override
    public Map<String, Object> queryForMap(String sql, Object... args){
        Map<String,Object> row = new HashMap<>();
        List<Map<String,Object>> rows = queryForListMap(sql,args);
        if(!rows.isEmpty() && rows != null){
            row = rows.get(0);
        }
        return row;
    }

    /**
     * 通过sql查询，并把结果集填充到对应类型的实体对象中。<BR/>
     * 填充规则：[实体属性名称与结果集字段 	  1.不分大小写  2.驼峰格式相互可以转成 相对应],找到便填充。
     *
     * @param sql  查询的SQL语句 ;SQL语句形式：(select * from T where Id=? and Name=? 或者 select * from T where Id='1' and Name='A') <br/>
     * @param type 实体对象的类型（如：T.class）
     * @param args 查询可变参数
     * @return 单个实体对象
     * @throws Exception
     */
    @Override
    public <T> T queryForObject(String sql, Class<T> type, Object... args){
        Map<String,Object> row = queryForMap(sql,args);
        return ConvertUtils.dataMapToObject(row,type);
    }


    /**
     * 执行指定数据库连接字符串的命令,返回结果集中的第一行第一列.
     *
     * @param sql  SQL语句,SQL语句形式：(select * from T where Id=? and Name=?)
     * @param args 查询可变参数
     * @return 结果集中的第一行第一列的值
     * @throws Exception
     */
    @Override
    public Object queryForScalar(String sql, Object... args) {
        Object retval = null;
        Map<String,Object> row = queryForMap(sql,args);
        if(null != row && row.size() >0){
            for(Map.Entry entry : row.entrySet()) {
                retval = entry.getValue();
                break;
            }
        }
        return retval;
    }

    /**
     * 分页的形式返回SQL查询结果
     *
     * @param page 分页对象
     * @param sql  查询语句 SQL语句形式：(select * from T where Id=? and Name=?)
     * @param type 实体对象的类型（如：T.class）
     * @param args 查询可变参数
     * @return
     * @throws Exception
     */
    @Override
    public <T> IPage<T> queryForPage(IPage page, String sql, Class<T> type, Object... args) {
        Long offset = page.offset();
        Long size = page.getSize();

        Object obj = queryForScalar("select count(1) from (" + sql + ") t",args);
        Long total = ObjectUtils.isEmpty(obj) ? 0L : Long.valueOf(String.valueOf(obj));

        page.setTotal(total);


        sql = sql + " limit "+ offset +"," + size;

        //如果有排序字段
        if(!CollectionUtils.isEmpty(page.orders())){
            Select select = null;
            try {
                select = (Select) CCJSqlParserUtil.parse(sql);
            } catch (JSQLParserException e) {
                throw new RuntimeException(e);
            }
            select.getSelectBody().accept(new SelectVisitorAdapter(){
                @Override
                public void visit(PlainSelect plainSelect) {

                    String tableName = plainSelect.getFromItem().toString();
                    if (plainSelect.getFromItem().getAlias()!=null) {
                        tableName = plainSelect.getFromItem().getAlias().getName();
                    }
                    List<OrderByElement> orderByElements = new ArrayList<>();

                    for (Object orderItem : page.orders()) {
                        OrderItem o = (OrderItem)orderItem;
                        OrderByElement orderByElement = new OrderByElement();
                        orderByElement.setAsc(o.isAsc());
                        orderByElement.setExpression(new Column(new Table(tableName),o.getColumn()));
                        orderByElements.add(orderByElement);
                    }
                    if(!CollectionUtils.isEmpty(orderByElements)) {
                        plainSelect.addOrderByElements(orderByElements);
                    }
                }
            });
            sql = select.toString();
        }


        List<Map<String,Object>> list = this.queryForListMap(sql, args);
        List<T>  ls = ConvertUtils.dataMapListToObjects(list, type);
        page.setRecords(ls);

        return page;
    }

    /**
     * 通过预编译的SQL查询语句（防SQL注入）或存储过程执行 INSERT、UPDATE 或 DELETE 语句以及 SQL
     * DDL（数据定义语言）语句。
     *
     * @param sql  SQL语句,SQL语句形式：(insert into T(C1,C2,C3) values(?,?,?))
     * @param args 可变参数
     * @return 返回执行sql操作所影响的行数 【定义 -1 为操作失败】
     * @throws Exception
     */
    @Override
    public int excuteNonQuery(String sql, Object... args) {
        int i = -1;
        PreparedStatement preSm = null;
        SqlSession session = getSqlSession();
        try {
            preSm = prepareStatement(session.getConnection(),sql,args);
            i = preSm.executeUpdate();
            log.info("执行数据库操作  执行语句:{} 参数列表:{}",sql,args);
        } catch (SQLException e) {
            log.info("执行数据库操作异常 {}  执行语句:{} 参数列表:{}",e,sql,args);
            e.printStackTrace();
        }finally {
            if(preSm!=null){
                try {
                    preSm.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            closeSqlSession(session);
        }
        return i;
    }


}